<!DOCTYPE html>
<html>

<head>
    <title>汇文投标施组</title>
    <link rel="icon" href="/logo.png" type="image/x-icon" />
    <meta charset="UTF-8">
    <meta http-equiv="Cache-Control" content="only-if-cached">

    <link rel="stylesheet" href="genStyle.css">
    <script type="text/javascript" src="vue2.7.js"></script>
    <link href="https://cdn.staticfile.org/twitter-bootstrap/5.1.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.staticfile.org/twitter-bootstrap/5.1.1/js/bootstrap.bundle.min.js"></script>
    <script type="text/javascript" src="model.js"></script>
    <script type="text/javascript" src="model2.js"></script>
    <script type="text/javascript" src="collapse.js"></script>
    <script src="jspdf.min.js"></script>
    <!--    <script src="font-han-sans.js"></script>-->
    <script src="font-simsun-normal.js"></script>
    <!--    <script src="simsun-bold.js"></script>-->
    <script src="font-simsun-bold.js"></script>
    <!--    <script src="jspdf-autotable-3.2.4.js"></script>-->
    <script src="jspdf-autotable-3.2.10.js"></script>
    <!--    <script src="jspdf-autotable-3.2.15.js"></script>-->
    <!--    taost-->
    <!--    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>-->
    <!--    <link href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css" rel="stylesheet">-->
    <!--    <script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js"></script>-->
    <script src="toast-jquery-3.4.1.js"></script>
    <link rel="stylesheet" type="text/css" href="toast.css">
    <script src="toast.js"></script>
    <script src="config.js"></script>
    <!-- config item template -->
    <script type="text/x-template" id="item-template">
    <li>
      <div
        class="subs_item"
        @click="checked"
        >
        <input :id="'input'+item.name" type="text" autocomplete="off" name="text" class="input" :placeholder="item.alias || item.name" disabled>

        <!-- <span v-if="isFolder">[{{ isOpen ? '-' : '+' }}]</span> -->
        <span v-if="item.lv>0" class="subs_span-action" @click="editName(item)">[修改]</span>
        <span v-if="item.lv<3" class="subs_span-action" @click="newChild">[配置]</span>
        <span v-if="item.lv>0" class="subs_span-action" @click="deleteItem(item)">[删除]</span>

      </div>
      <ul>
        <tree-item
          v-for="(child, index) in item.children"
          :key="index"
          :item="child"
          @new-child="$emit('new-child', $event)"
        ></tree-item>
        <!-- <li class="add" @click="$emit('add-item', item)">+</li> -->
      </ul>
    </li>
</script>

    <!-- count item template -->
    <script type="text/x-template" id="item-templatec">
        <li>
          <div
            class="subs_item"
            @click="checked"
            >
            <input :id="'inputc'+item.name" type="text" autocomplete="off" name="text" class="input" :placeholder="item.alias || item.name" disabled>
    
            <span v-if="!item?.children?.length" :style="{ color: item.count>0 ? 'green' : 'red' }">{{item.countStr}}</span>
    
          </div>
          <ul>
            <tree-itemc
              v-for="(child, index) in item.children"
              :key="index"
              :item="child"
              @new-child="$emit('new-child', $event)"
            ></tree-itemc>
            <!-- <li class="add" @click="$emit('add-item', item)">+</li> -->
          </ul>
        </li>
    </script>

</head>

<body style="background: #ddd;">
    <div id="app" style="height: 100%;margin-top: 0xp;">
        <div>
            <div style="display: flex; position: absolute; top: 10px; right: 20px;">
                <span style="color: #1ba035;">{{"当前用户: " + tokenObj.username }}</span>
                <a href="info.html" style="margin-right: 20px;margin-left: 50px;" target="_blank">使用说明</a>
                <a href="manage.html" style="margin-right: 20px;" target="_blank">模版管理</a>
                <a href="#" onclick="logout();">退出登陆</a>

            </div>

            <!-- <a href="info.html" style="margin: 15px;" target="_blank">使用说明</a> -->
            <h3 class="text-center" style="color:#555;">{{stepTitle}}</h3>
            <div style="display: flex;justify-content: center;">
                <span>{{name + " ---- " + getTypeName()}}</span>
            </div>

            <div v-show="currentStep===1" class="column"
                style="margin: 0 auto;margin-top: 1.5rem;width: 80%;display: block;" id="step1">

                <div class="input-group mb-2 center" style="margin-top: 25px;">
                    <span class="input-group-text">项目名称(必填)：</span>
                    <input v-model="name" type="text" class="form-control" id="gcxx1">
                </div>

                <div class="input-group mb-2">
                    <span class="input-group-text">建设地点(必填)：</span>
                    <input v-model="address" type="text" class="form-control" id="gcxx2">
                </div>

                <div class="input-group mb-2">
                    <span class="input-group-text">计划开工时间：</span>
                    <input v-model="start_date" type="date" class="form-control" id="gcxx3">
                </div>

                <div class="input-group mb-2">
                    <span class="input-group-text">计划竣工时间：</span>
                    <input v-model="end_date" type="date" class="form-control" id="gcxx4">
                </div>

                <div class="input-group mb-2">
                    <span class="input-group-text">建设规模(必填)：</span>
                    <input v-model="jsgm" type="text" class="form-control" id="gcxx5">
                </div>

                <div class="input-group mb-2">
                    <span class="input-group-text">标段划分：</span>
                    <input v-model="bdhf" type="text" class="form-control" id="gcxx6">
                </div>

                <div class="input-group mb-2">
                    <span class="input-group-text">资金来源：</span>
                    <input v-model="zjly" type="text" class="form-control" id="gcxx7">
                </div>

                <div class="input-group mb-2">
                    <span class="input-group-text">质量标准(必填)：</span>
                    <input v-model="zlbz" type="text" class="form-control" id="gcxx8">
                </div>

                <div class="input-group mb-2">
                    <span class="input-group-text">招标范围：</span>
                    <input v-model="zbfw" type="text" class="form-control" id="gcxx9">
                </div>

                <div class="input-group mb-2">
                    <span class="input-group-text">出资比例：</span>
                    <input v-model="czbl" type="text" class="form-control" id="gcxx10">
                </div>

                <div class="input-group mb-2">
                    <span class="input-group-text">资金落实情况：</span>
                    <input v-model="zjlsqk" type="text" class="form-control" id="gcxx11">
                </div>

                <div class="input-group mb-3">
                    <span class="input-group-text">工期(必填)：</span>
                    <input v-model="duration" type="text" class="form-control" id="gcxx12">
                </div>

                <div class="input-group mb-3">
                    <span class="input-group-text">行间距：</span>
                    <input v-model="linespace" type="number" class="form-control" id="gcxx13">
                </div>
            </div>

            <div v-show="currentStep===2" class="row" style="margin-top: 28px;display: none;width: 100%" id="step2">
                <div class="radio-inputs" style="margin-top: 60px;">
                    <label>
                        <input checked class="radio-input" type="radio" id="8" value="8" name="projectType"
                            onchange="onTypeRadioChanged(this)">
                        <span class="radio-tile">
                            <span class="title-span">建筑工程</span>
                            <p class="radio-label">民用建筑、工业建筑、构筑物以及相配套工程</p>
                        </span>
                    </label>

                    <label>
                        <input class="radio-input" type="radio" id="2" value="2" name="projectType"
                            onchange="onTypeRadioChanged(this)">
                        <span class="radio-tile">
                            <span class="title-span">水利水电工程</span>
                            <p class="radio-label">防洪、灌溉、发电、供水、治涝、水环境治理等工程</p>
                        </span>
                    </label>

                </div>

                <div class="radio-inputs" style="margin-top: 6px;">
                    <label>
                        <input class="radio-input" type="radio" id="4" value="4" name="projectType"
                            onchange="onTypeRadioChanged(this)">
                        <span class="radio-tile">
                            <span class="title-span">市政公用工程</span>
                            <p class="radio-label">道路、绿化、管网、给排水、改造、生态修复</p>
                        </span>
                    </label>

                    <label>
                        <input class="radio-input" type="radio" id="1" value="1" name="projectType"
                            onchange="onTypeRadioChanged(this)">
                        <span class="radio-tile">
                            <span class="title-span">公路工程</span>
                            <p class="radio-label">
                                路基工程、路面工程、桥梁、涵洞工程、隧道工程、安全设施及预埋管线工程、绿化及环境保护设施工程</p>
                        </span>
                    </label>
                </div>
            </div>

            <div v-show="currentStep===3" class="row" style="margin-top: 28px;display: none;width: 100%" id="step3">
                <div class="radio-inputs1" style="margin: 6px;">
                    <label v-for="(item, index) in contents" style="margin: 6px;">
                        <input :checked="index===selectedContentsId" class="radio-input" type="radio" :id="item._id"
                            :value="index" name="item._id" onchange="onContentsRadioChanged(this)">
                        <span class="radio-tile">
                            <span class="title-span">{{item.name}}</span>
                            <span v-for="(chapter, i) in item.chapters" class="radio-label">
                                {{"第"+(i+1)+"章 "+chapter}}
                            </span>
                        </span>
                    </label>
                </div>
            </div>

            <div v-show="currentStep===4" style="margin-top: 28px;display: none;width: 100%" id="step4">
                <div style="display: flex;margin-top: 15px;">

                    <ul class="subs" style="margin: 6px;width: 60%;">
                        <tree-item :item="treeData" @new-child="newChild"></tree-item>
                    </ul>

                    <div v-show="startConfig" style="width: 40%;">
                        <h6 style="color: #555;">{{pickSubsTitle}}</h6>
                        <div v-if="loadingOptions" class="spinner-border" role="status">
                        </div>
                        <ul class="list-group">
                            <li v-for="item in configItem.options" class="list-group-item">
                                <input class="form-check-input" :value="item" v-model="configItem.children"
                                    :id="item.name" @change="handleChange($event)" type="checkbox">
                                {{item.name}}
                            </li>
                        </ul>
                        <p v-if="!configItem.options || configItem.options.length<1">没有找到相关模版</p>
                    </div>
                </div>
            </div>

            <div v-show="currentStep===5" style="margin-top: 28px;display: none;width: 100%" id="step5">
                <div style="display: flex;margin-top: 15px;">
                    <ul class="subs" style="margin: 6px;width: 50%;">
                        <tree-itemc :item="treeData" @new-child="newChild"></tree-itemc>
                    </ul>

                    <div style="width: 50%;">
                        <div style="display: flex;margin-top: 15px;">
                            <button type="button" class="btn btn-primary" style="margin-bottom: 8px"
                                onclick="loadModels()">预览</button>
                            <button id="export_pdf" type="button" class="btn btn-success"
                                style="margin-left: 15px;margin-bottom: 8px" @click="genPDF"
                                :disabled="state!=='complete'" v-if="retLi.length>0">导出PDF
                            </button>
                        </div>

                        <p>
                        <div v-if="loadingModels" class="spinner-border" role="status"> </div>
                        </p>

                        <div class="rightDiv" ref="rightDiv">
                            <div id="resultLi"
                                style="background-color:white;margin-top: 25px;border-radius:25px;padding:16px;"
                                v-if="retLi.length>0">
                                <ul style="list-style: none;margin-top:0;padding-top:0;width: 100%;">
                                    <li v-for="(obj,i) in retLi" style="padding-top: 16px;padding-bottom: 16px">
                                        <div style="display: flex;flex-direction: row" v-if="obj">
                                            <div style="display: flex;flex-direction: column;">
                                                <span
                                                    style="font-size: 22px;color:black;font-weight: 400;line-height: 23px;">
                                                    {{ formatTitle(i, obj.alias || obj.name) }}
                                                </span>
                                                <!-- <pre style="margin-top: 15px; white-space: pre-wrap;max-width: 700px">  -->

                                                <div ref="divrefs">
                                                    {{
                                                    formatModelPreview(i,obj.content)
                                                    }}
                                                </div>

                                                <!-- </pre>  -->
                                            </div>
                                        </div>
                                    </li>
                                </ul>
                            </div>
                        </div>

                    </div>

                </div>
            </div>

            <div id="popup" class="popup">
                <span style="color: white;font-size: 25px;height: 25px;padding: 25px">自定义章节内容</span>
                <div style="display: flex;flex-direction: row;height: 48px; line-height: 48px">
                    <button type="button" style="margin-left: 25px" onclick="confirConfig2()">确定
                    </button>
                    <button type="button" style="margin-left: 25px" onclick="closePopup()">关闭
                    </button>
                </div>
                <div class="row" style="padding-top: 25px;text-align: left">
                    <div class="col">
                        <div
                            style="display: flex;flex-direction: row;height: 48px;justify-content: center;text-align: center; line-height: 48px">
                            <h4 algin="center">选择小节模版</h4>
                        </div>
                        <div class="card border-info bg-light"
                            style="margin-top: 25px;border-radius:25px;padding: 1.5% 3% 1.5% 3%">
                            <ul class="list-group" style="list-style: none;margin-top:0;padding:0%;">
                                <li v-for="obj in availableLi2" style="margin-top: 1.5%;margin-bottom: 1.5%;padding:0">
                                    <div class="card" style="border-radius:25px;padding: 16px;background: white">
                                        <div style="display: flex;flex-direction: row" v-if="obj">
                                            <div style="display: flex;flex-direction: column;width: 84%">
                                                <span
                                                    style="font-size: 22px;color:black;font-weight: 400;line-height: 23px;">
                                                    {{ obj.contentCode +"："
                                                    + obj.title }}
                                                </span>
                                                <span style="margin-top: 10px">内容模版:</span>
                                                <span style="margin-top: 10px">{{obj.content.substring(0,25)}}</span>
                                            </div>
                                            <button type="button" class="btn btn-block btn-primary"
                                                @click="useModel2(obj)"
                                                style="width: 14%;border-radius:15px;margin-left:2%;margin-top:15px;margin-bottom:15px;">
                                                使用
                                            </button>
                                        </div>

                                    </div>

                                </li>
                            </ul>
                        </div>
                    </div>

                    <div class="col">
                        <div
                            style="display: flex;flex-direction: row;height: 48px;justify-content: center;text-align: center; line-height: 48px">
                            <h4 style="align-content: center">结果预览</h4>
                        </div>
                        <div id="resultLi2" class="card"
                            style="background-color:white;margin-top: 25px;border-radius:25px;padding:16px;"
                            v-if="retLi2.length>0">
                            <span style="font-size: 22px;color:black;font-weight: 400;line-height: 23px;">
                                {{ customChapter.title }}
                            </span>
                            <ul style="list-style: none;margin-top:0;padding-top:0;">
                                <li v-for="(obj,i) in retLi2" style="padding-top: 16px;padding-bottom: 16px">
                                    <div style="display: flex;flex-direction: row" v-if="obj">
                                        <div style="display: flex;flex-direction: column;">
                                            <pre style="margin-top: 15px; white-space: pre-wrap;max-width: 700px">{{
                                            formatTitle2(i, obj.title)+"\n"+formatModel(obj.content)
                                        }}</pre>
                                        </div>
                                    </div>
                                </li>
                            </ul>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <div style="display: flex;justify-content:space-between;padding-left: 10%;padding-right: 10%;position: fixed;
        bottom: 30px;left: 0; width: 100%;">
            <button v-show="currentStep!==1" class="BtnPrev" id="btnPrev" onclick="prevStep()">
                <p class="btnp_txt">上一步</p>
                <span class="effectp"></span>
            </button>

            <button v-show="currentStep!==steps.length" class="Btn" style="margin-left: auto;" id="btnNext"
                onclick="nextStep()">
                <p class="btn_txt">下一步</p>
                <span class="effect"></span>
            </button>
        </div>

    </div>
    <script>
        toastr.options = {
            // toastr配置
            "closeButton": true, //是否显示关闭按钮
            "debug": false, //是否使用debug模式
            "progressBar": true, //是否显示进度条，当为false时候不显示；当为true时候，显示进度条，当进度条缩短到0时候，消息通知弹窗消失
            "preventDuplicates": true,
            "positionClass": "toast-bottom-center",//显示的动画位置
            "showDuration": "400", //显示的动画时间
            "hideDuration": "1000", //消失的动画时间
            "timeOut": "7000", //展现时间
            "extendedTimeOut": "1000", //加长展示时间
            "showEasing": "swing", //显示时的动画缓冲方式
            "hideEasing": "linear", //消失时的动画缓冲方式
            "showMethod": "fadeIn", //显示时的动画方式
            "hideMethod": "fadeOut" //消失时的动画方式
        }

        //调用方式
        // toastr.error("错误");
        // toastr.success("成功")
        // toastr.warning("失败")
        // toastr.info("你好")
    </script>

    <script>
        var tokenInfo = localStorage.getItem('tokenInfo');
        var tokenObj = JSON.parse(tokenInfo);
        // let currentStep = 1;
        let prevBtn = document.getElementById("btnPrev");
        let nextBtn = document.getElementById("btnNext");
        let step1 = document.getElementById("step1");
        let step2 = document.getElementById("step2");
        let step3 = document.getElementById("step3");
        let step4 = document.getElementById("step4");
        let step5 = document.getElementById("step5");
        let steps = [step1, step2, step3, step4, step5];
        // 建筑，水利，市政，公路 分别对应四个二进制位 1111
        let projectType = '8'

        function nextStep() {
            let finishStep1 = true;
            if (app.currentStep === 1) {
                finishStep1 =
                    !!(app.name?.trim()) && !!(app.address?.trim()) && !!(app.jsgm?.trim())
                    && !!(app.zlbz?.trim()) && !!(app.duration?.trim());
            }
            if (!finishStep1) {
                toastr.error('请填写所有必填项')
                return;
            }

            if (app.currentStep < steps.length) {
                app.currentStep++;
                // console.log("currentStep: " + app.currentStep)
            }
            if (app.currentStep === steps.length) {
                nextBtn.style.display = "none";
            }
            onPageShow();
        }

        function prevStep() {
            if (app.currentStep > 1) {
                app.currentStep--;
                // console.log("currentStep: " + app.currentStep)
            }
            if (app.currentStep === 1) {
                prevBtn.style.display = "none";
            }
            onPageShow();
        }

        function onPageShow() {
            if (app.currentStep === 1) {
                app.stepTitle = '一 填写工程信息'
            }
            // 专业分类
            if (app.currentStep === 2) {
                app.stepTitle = '二 选择工程专业'
                // app.selectedContentsId = 0;
            }
            // 目录大纲
            if (app.currentStep === 3) {
                app.stepTitle = '三 选择大纲目录'
                if (app.contents.length === 0) {
                    loadContents();
                }
            }
            // console.log('selectedContentsId:' + app.selectedContentsId)
            // 内容定制
            if (app.currentStep === 4) {
                app.stepTitle = '四 定制内容';
                app.retLi = [];
            }
            if (app.currentStep === 5) {
                app.stepTitle = '五 检查可用模版数'
                queryCount(app.treeData);
            }
        }

        function onTypeRadioChanged(radio) {
            let newValue = radio.value;
            if (projectType !== newValue) {
                resetContents();
                projectType = radio.value;
                loadContents();
            }
            // console.log('projectType:' + projectType)
        }

        function resetContents() {
            // console.log('resetContents')
            app.contents = [];
            app.selectedContentsId = 0;
            resetTree();
        }

        function resetTree() {
            app.startConfig = false;
            app.treeData = {};
            app.retLi = [];
        }

        function updateTree() {
            resetTree();
            app.treeData = {
                name: app.contents[app.selectedContentsId].name,
                lv: 0,
                children: []
            }
            app.treeData.children = app.contents[app.selectedContentsId].chapters.map(c => {
                return {
                    name: c,
                    lv: 1,
                    children: []
                }
            });
            app.treeData.options = app.contents[app.selectedContentsId].chapters.map(c => {
                return {
                    name: c,
                    lv: 1,
                    children: []
                }
            });
            // console.log('treeData')
        }

        function onContentsRadioChanged(radio) {
            app.selectedContentsId = radio.value;
            updateTree();
            console.log('onContentsRadioChanged:' + app.selectedContentsId)
        }

        function getTypeName() {
            let str = '';
            switch (projectType) {
                case '8':
                    str = '建筑工程';
                    break;
                case '2':
                    str = '水利水电';
                    break;
                case '4':
                    str = '市政';
                    break;
                case '1':
                    str = '公路';
                    break;
            }
            return str;
        }

        function loadContents() {
            let str = getTypeName();
            if (tokenInfo) {
                // console.log('loadContents type: ' + projectType);
                const postStr = tokenInfo.substring(0, tokenInfo.length - 1) + `,"type":"` + projectType + `"}`
                const xhr = new XMLHttpRequest()
                xhr.open('POST', baseUrl + '/chapters')
                xhr.setRequestHeader('Content-Type', 'application/json');
                xhr.onreadystatechange = async function () {
                    if (xhr.readyState === 4) {
                        if (xhr.status === 200) {
                            // console.log('Server response:', xhr.responseText);
                            let resp = JSON.parse(xhr.responseText);
                            if (resp.code === 200) {
                                app.contents = resp.res;
                                updateTree();
                            } else {
                                toastr.error(resp.msg);
                            }
                        } else {
                            toastr.error('请求失败：' + xhr.statusText);
                        }
                    }
                }
                xhr.send(postStr);
            } else {
                console.log('go login')
                window.location.href = "./login.html"
            }
        }

        function loadModels() {
            // fetch models by net
            app.loadingModels = true;
            let promises = [];
            loadModelsInner(app.treeData, promises);
            Promise.all(promises).then(() => {
                app.loadingModels = false;
                convertData(app.treeData);
                console.log('所有叶子节点更新完成');
                toastr.clear();
                toastr.success("更新完成");
            });
        }

        function convertData(root) {
            app.retLi = [];
            let chapters = root.children;
            for (let chapter of chapters) {
                if (chapter?.children?.length) {
                    // 用节组装章
                    let tmp = '';
                    for (let i = 0; i < chapter.children.length; i++) {
                        let sub1 = chapter.children[i];
                        tmp += "{#" + app.formatTitle2(i, sub1.alias || sub1.name) + "}\n";

                        if (sub1?.children?.length) {
                            // 用小节组装节
                            let tmp2 = '';
                            for (let i = 0; i < sub1.children.length; i++) {
                                let sub2 = sub1.children[i];
                                tmp2 += "{##" + formatTitle3(i, sub2.alias || sub2.name) + "}\n";
                                tmp2 += sub2.content || '' + "\n";
                            }
                            sub1.content = tmp2;
                        }
                        tmp += sub1.content || '' + "\n";
                    }
                    chapter.content = tmp;
                }
                app.retLi.push(chapter);
            }
            console.log('convertData done');
        }

        function updateNode(node) {
            return new Promise((resolve, reject) => {
                if (node.children?.length) {
                    console.log('has children');
                    // reject(new Error('no available model'));
                    resolve();
                    return;
                }
                if (!node.count) {
                    console.log('no available model');
                    // reject(new Error('no available model'));
                    resolve();
                    return;
                }
                if (!tokenInfo) {
                    console.log('token invalid');
                    // reject(new Error('token invalid'));
                    resolve();
                    return;
                }
                // fetch models by net
                const postStr = tokenInfo.substring(0, tokenInfo.length - 1) + `,"type":"` + projectType + `","lv":"` + node.lv + `","title":"` + node.name + `"}`

                fetch(baseUrl + '/pickModel', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                        // Include token if necessary (assuming tokenInfo contains token)
                        // Authorization: `Bearer ${tokenInfo}`, // Adjust based on your token format
                    },
                    body: postStr,
                })
                    .then(response => {
                        if (!response.ok) {
                            throw new Error(`Network response was not ok: ${response.statusText}`);
                        }
                        return response.json();
                    })
                    .then(resp => {
                        if (resp.code === 200) {
                            let res = resp.res;
                            Vue.set(node, "content", res.content);
                            console.log('fetch content:', node.name);
                            resolve(); // Resolve the promise on successful update
                        } else {
                            toastr.error(resp.msg);
                            reject(new Error(`API error: ${resp.msg}`)); // Reject on API errors
                        }
                    })
                    .catch(error => {
                        console.error('Error fetching model:', error);
                        // toastr.error('Failed to fetch model');
                        reject(error); // Reject the promise on errors
                    });
            });
        }

        function loadModelsInner(node, promises) {
            if (node.children?.length) {
                for (let e of node.children) {
                    loadModelsInner(e, promises);
                }
            } else {
                promises.push(updateNode(node));
            }
        }

        function queryCount(root) {
            if (root.children?.length) {
                let nodes = root.children;
                for (let node of nodes) {
                    queryCount(node);
                }
            } else {
                // 叶子节点查询可用模版数
                if (tokenInfo) {
                    Vue.set(root, "countStr", `正在查询模版数...`);
                    const postStr = tokenInfo.substring(0, tokenInfo.length - 1) + `,"type":"` + projectType + `","lv":"` + root.lv + `","title":"` + root.name + `"}`
                    const xhr = new XMLHttpRequest()
                    xhr.open('POST', baseUrl + '/checkCount')
                    xhr.setRequestHeader('Content-Type', 'application/json');
                    xhr.onreadystatechange = async function () {
                        app.loadingOptions = false;
                        if (xhr.readyState === 4) {
                            if (xhr.status === 200) {
                                // console.log('Server checkCount:', xhr.responseText);
                                let resp = JSON.parse(xhr.responseText);
                                if (resp.code === 200) {
                                    if (resp.count === 0) {
                                        // app.treeData.ready = false;
                                    }
                                    Vue.set(root, "count", resp.count);
                                    Vue.set(root, "countStr", `[模版数${resp.count}]`);
                                } else {
                                    toastr.error(resp.msg);
                                }
                            } else {
                                toastr.error('请求 checkCount 失败：' + xhr.statusText);
                            }
                        }
                    }
                    xhr.send(postStr);
                } else {
                    console.log('go login')
                    window.location.href = "./login.html"
                    app.loadingOptions = false;
                }
            }
        }

        // define the tree-item component
        Vue.component("tree-item", {
            template: "#item-template",
            props: {
                item: Object,
            },
            data: function () {
                return {
                    isOpen: true,
                    // checkedItem: null,
                };
            },
            computed: {
                isFolder: function () {
                    return this.item.children && this.item.children.length;
                },
            },
            methods: {
                // toggle: function () {
                //   if (this.isFolder) {
                //     this.isOpen = !this.isOpen;
                //   }
                // },

                checked: function () {
                    // this.checkedItem = this.item;
                },

                newChild: function () {
                    if (this.item.lv < 3) {
                        this.$emit("new-child", this.item);
                    }
                },
                editName: function (it) {
                    console.log('edit:' + it.name)
                    // 获取input元素和按钮元素
                    const input = document.getElementById('input' + it.name);
                    // const enableButton = document.getElementById('btnEdit' + id);
                    input.disabled = false;
                    input.focus();

                    input.addEventListener('blur', function (event) {
                        console.log('Input失去焦点了');
                        it.alias = input.value;
                        input.disabled = true;
                        // 当input元素失去焦点时执行的操作
                    });
                },
                isSame: function (n, d) {
                    if (n && d && n.lv === d.lv && n.name === d.name) {
                        return true;
                    }
                    return false;
                },
                delInner: function (tree, node) {
                    // 遍历当前节点的子节点
                    if (tree.children && tree.children.length > 0) {
                        if (tree.lv < (node.lv - 1)) {
                            for (let child of tree.children) {
                                this.delInner(child, node);
                            }
                        }
                        if (tree.lv === (node.lv - 1)) {
                            for (let i = 0; i < tree.children.length; i++) {
                                if (this.isSame(tree.children[i], node)) {
                                    tree.children[i] = null;
                                }
                            }
                        }
                        tree.children = tree.children.filter(child => child !== null && child !== undefined);
                    }
                },
                deleteItem: function (node) {
                    // 如果当前节点为空或节点不匹配,直接返回
                    // let tree = treeData;
                    if (app.treeData && node) {
                        this.delInner(app.treeData, node);
                    }
                    console.log('done')
                }
            },
        });

        Vue.component("tree-itemc", {
            template: "#item-templatec",
            props: {
                item: Object,
                fetchCount: -1
            },
            data: function () {
                return {
                    isOpen: true,
                    // checkedItem: null,
                };
            },
            computed: {
                isFolder: function () {
                    return this.item.children && this.item.children.length;
                },
            },
            methods: {
                // toggle: function () {
                //   if (this.isFolder) {
                //     this.isOpen = !this.isOpen;
                //   }
                // },

                checked: function () {
                    // this.checkedItem = this.item;
                },

                newChild: function () {
                    if (this.item.lv < 3) {
                        this.$emit("new-child", this.item);
                    }
                },
                isSame: function (n, d) {
                    if (n && d && n.lv === d.lv && n.name === d.name) {
                        return true;
                    }
                    return false;
                },
                delInner: function (tree, node) {
                    // 遍历当前节点的子节点
                    if (tree.children && tree.children.length > 0) {
                        if (tree.lv < (node.lv - 1)) {
                            for (let child of tree.children) {
                                this.delInner(child, node);
                            }
                        }
                        if (tree.lv === (node.lv - 1)) {
                            for (let i = 0; i < tree.children.length; i++) {
                                if (this.isSame(tree.children[i], node)) {
                                    tree.children[i] = null;
                                }
                            }
                        }
                        tree.children = tree.children.filter(child => child !== null && child !== undefined);
                    }
                },
                deleteItem: function (node) {
                    // 如果当前节点为空或节点不匹配,直接返回
                    // let tree = treeData;
                    if (app.treeData && node) {
                        this.delInner(app.treeData, node);
                    }
                    console.log('done')
                }
            },
        });

        var app = new Vue({
            el: "#app",
            data: {
                models: [],
                shortModels: [],
                models2: [],
                retLi: [],
                retLi2: [],
                customChapter: null,
                availableLi2: [],
                selected: -1,
                currentStep: 1,
                contents: [],
                selectedContentsId: 0,
                pickSubsTitle: "",
                stepTitle: "一 填写工程信息",
                startConfig: false,
                startFetch: false,
                loadingModels: false,
                loadingOptions: false,
                isAdmin: tokenObj.admin,
                treeData: {},
                configItem: {},
                name: "XX项目",
                address: "成都市",
                jsgm: "",
                bdhf: "",
                zjly: "",
                zlbz: "",
                zbfw: "",
                czbl: "",
                zjlsqk: "",
                start_date: "2023-05-01",
                end_date: "2023-05-01",
                duration: "5天",
                linespace: 0,
                export_btn_txt: "导出PDF",
                state: "",
                isActive: true
            },
            methods: {
                changeDivStatus: function () {
                    this.isActive = !this.isActive;

                    //console.log('this.$refs.leftDiv='+this.$refs.leftDiv);

                    if (this.isActive) {
                        this.$refs.leftDiv.class = "leftDiv";
                    }
                    else {
                        this.$refs.leftDiv.class = "leftDivHidden";
                    }
                },
                selectOption: function () {

                },
                // useModel: function (model) {
                //     //console.log('aaaeeeee');
                //     if (model.inUse) {
                //         this.retLi = this.retLi.filter(e => e.contentCode !== model.contentCode)
                //     } else {
                //         this.retLi.push(model)
                //     }
                //     model.inUse = !model.inUse
                // },
                // useModel2: function (model) {
                //     this.retLi2.push(model)
                // },
                formatTitle: function (i, title) {
                    if (i < 10) {
                        return first + arr[i] + end + title
                    }
                    let high = Math.floor(i / 10)
                    let low = i % 10
                    //console.log("ten:" + high + " Low: " + low)
                    if (low === 9) {
                        return first + arr[high] + ten + end + title
                    }
                    if (high === 1) {
                        return first + ten + arr[low] + end + title
                    }
                    return first + arr[high - 1] + ten + arr[low] + end + title
                },
                formatTitle2: function (i, title) {
                    if (i < 10) {
                        return first + arr[i] + end2 + title
                    }
                    let high = Math.floor(i / 10)
                    let low = i % 10
                    console.log("ten:" + high + " Low: " + low)
                    if (low === 9) {
                        return first + arr[high] + ten + end2 + title
                    }
                    if (high === 1) {
                        return first + ten + arr[low] + end2 + title
                    }
                    return first + arr[high - 1] + ten + arr[low] + end2 + title
                },
                formatModel: function (content) {
                    if (!content) {
                        return "";
                    }
                    var ret = content
                    ret = ret.replaceAll("{{name}}", this.name)
                    ret = ret.replaceAll("{{address}}", this.address)
                    ret = ret.replaceAll("{{start_date}}", this.start_date)
                    ret = ret.replaceAll("{{end_date}}", this.end_date)
                    ret = ret.replaceAll("{{duration}}", this.duration)
                    ret = ret.replaceAll("{{jsgm}}", this.jsgm)
                    ret = ret.replaceAll("{{bdhf}}", this.bdhf)
                    ret = ret.replaceAll("{{zjly}}", this.zjly)
                    ret = ret.replaceAll("{{zlbz}}", this.zlbz)
                    ret = ret.replaceAll("{{zbfw}}", this.zbfw)
                    ret = ret.replaceAll("{{czbl}}", this.czbl)
                    ret = ret.replaceAll("{{zjlsqk}}", this.zjlsqk)
                    // ret = ret.trim()
                    return ret
                },
                setDivId: function (i) {
                    console.log("div_", i);
                    return "div_" + i;
                },
                //页面预览，处理图片
                formatModelPreview: function (seq, content) {
                    if (!content) {
                        return "";
                    }
                    var ret = content
                    ret = ret.replaceAll("{{name}}", this.name)
                    ret = ret.replaceAll("{{address}}", this.address)
                    ret = ret.replaceAll("{{start_date}}", this.start_date)
                    ret = ret.replaceAll("{{end_date}}", this.end_date)
                    ret = ret.replaceAll("{{duration}}", this.duration)
                    ret = ret.replaceAll("{{jsgm}}", this.jsgm)
                    ret = ret.replaceAll("{{bdhf}}", this.bdhf)
                    ret = ret.replaceAll("{{zjly}}", this.zjly)
                    ret = ret.replaceAll("{{zlbz}}", this.zlbz)
                    ret = ret.replaceAll("{{zbfw}}", this.zbfw)
                    ret = ret.replaceAll("{{czbl}}", this.czbl)
                    ret = ret.replaceAll("{{zjlsqk}}", this.zjlsqk)
                    // ret = ret.trim()

                    this.$nextTick(() => {
                        var div = this.$refs["divrefs"][seq];
                        div.innerHTML = "";
                    });

                    //console.log("aaaaaaaaaaaaaaaaaaaaSSSSSSSSSSSSSS")
                    var ind = 0;
                    while (ind < ret.length) {
                        var i = ret.indexOf("{\"pdfImgName", ind);
                        var txt = i;
                        var j = ret.indexOf("}", ind);
                        var m = ret.indexOf("{\"pdfTableName", ind);
                        var n = 0;

                        var flag = 0;
                        //无图片，也无table
                        if (i == -1 && m == -1) {
                            txt = ret.length;
                            j = ret.length;
                            flag = 0;
                        }
                        //无图片，有table
                        else if (i == -1 && m > -1) {
                            txt = m;
                            flag = 1;
                        }
                        //有图片，无table
                        else if (i > -1 && i < j && m == -1) {
                            txt = i;
                            flag = 2;
                        }
                        //先有图片，后有table
                        else if (i > -1 && i < j && m > j) {
                            txt = i;
                            flag = 3;
                        }
                        //先有table,后有图片
                        else if (i > -1 && m > -1 && i > j && m < j) {
                            txt = m;
                            flag = 4;
                        }




                        //console.log(txt+":SSSSSSSSSSSSSSS2,content:"+tx);
                        if (txt > -1) {
                            var txtcom = document.createElement('pre');
                            txtcom.innerHTML = ret.substring(ind, txt);
                            txtcom.style.marginTop = "15px";
                            txtcom.style.whiteSpace = "pre-wrap";
                            txtcom.style.maxWidth = "700px";

                            this.$nextTick(() => {
                                var div = this.$refs["divrefs"][seq];
                                //console.log('divid:',seq,div);
                                div.appendChild(txtcom);
                            })
                        }

                        ind = j;


                        //处理图片
                        if (flag == 2 || flag == 3) {
                            var cc = ret.substring(i, j + 1);
                            //console.log('bbb_____________',cc);
                            var json = JSON.parse(cc);
                            //console.log("pdfImgName",json.pdfImgName);
                            //console.log("width",json.width);
                            //console.log("height",json.height);
                            //console.log("src",json.src);
                            //console.log("height",json.height);

                            var img = "<img width=" + json.width + " height=" + json.height + " src=\"" + json.src + "\" alt=\"Generated\" />";

                            //console.log("替换结果",ret);
                            let imgDom = document.createElement('img')
                            imgDom.style.width = json.width;
                            imgDom.style.height = json.height;
                            imgDom.src = json.src;
                            //var div = document.getElementById(seq);

                            this.$nextTick(() => {
                                var div = this.$refs["divrefs"][seq];
                                //console.log('divid:',seq,div);                            
                                div.appendChild(imgDom);
                            })

                            //图片后接文字与table
                            if (flag == 3) {
                                //处理后接文字
                                var txtcom = document.createElement('pre');
                                txtcom.innerHTML = ret.substring(j, m);
                                txtcom.style.marginTop = "15px";
                                txtcom.style.whiteSpace = "pre-wrap";
                                txtcom.style.maxWidth = "700px";

                                this.$nextTick(() => {
                                    var div = this.$refs["divrefs"][seq];
                                    //console.log('divid:',seq,div);
                                    div.appendChild(txtcom);
                                })

                                //处理后接table
                                n = ret.indexOf("}", m);
                                var dd = ret.substring(m, n + 1);
                                //console.log('bbb_____________',cc);
                                var json2 = JSON.parse(dd);
                                //创建表格table
                                var tab = createTableDom(json);
                                this.$nextTick(() => {
                                    var div = this.$refs["divrefs"][seq];
                                    //console.log('divid:',seq,div);
                                    div.appendChild(tab);
                                })

                                ind = n;
                            }
                        }
                        //先table,后图片
                        else if (flag == 1 || flag == 4) {
                            //处理后接table
                            var cc = ret.substring(m, j + 1);
                            //console.log('bbb_____________',cc);
                            var json = JSON.parse(cc);
                            //创建表格table
                            console.log("json:", json);
                            var tab = createTableDom(json);
                            this.$nextTick(() => {
                                var div = this.$refs["divrefs"][seq];
                                //console.log('divid:',seq,div);
                                div.appendChild(tab);
                            })

                            ind = j;

                            if (flag == 4) {
                                //处理后接文字
                                var txtcom = document.createElement('pre');
                                txtcom.innerHTML = ret.substring(j, i + 1);
                                txtcom.style.marginTop = "15px";
                                txtcom.style.whiteSpace = "pre-wrap";
                                txtcom.style.maxWidth = "700px";

                                this.$nextTick(() => {
                                    var div = this.$refs["divrefs"][seq];
                                    //console.log('divid:',seq,div);
                                    div.appendChild(txtcom);
                                })

                                //处理后接图片
                                n = ret.indexOf("}", i);
                                cc = ret.substring(i, n + 1);
                                //console.log('bbb_____________',cc);
                                json = JSON.parse(cc);
                                //console.log("pdfImgName",json.pdfImgName);
                                //console.log("width",json.width);
                                //console.log("height",json.height);
                                //console.log("src",json.src);
                                //console.log("height",json.height);

                                //var img = "<img width="+json.width+" height="+json.height+" src=\"" + json.src + "\" alt=\"Generated\" />";

                                //console.log("替换结果",ret);
                                let imgDom = document.createElement('img')
                                imgDom.style.width = json.width;
                                imgDom.style.height = json.height;
                                imgDom.src = json.src;
                                //var div = document.getElementById(seq);

                                this.$nextTick(() => {
                                    var div = this.$refs["divrefs"][seq];
                                    //console.log('divid:',seq,div);                            
                                    div.appendChild(imgDom);
                                })

                                ind = n;
                            }


                        }



                        //console.log("ind:"+ind+",j:"+j);

                        //console.log(j+"=j,retl="+ret.length);
                        if (j + 1 < ret.length) {
                            var txtcom2 = document.createElement('pre');
                            var nextIndex = ret.indexOf("{\"pdfImgName", ind);
                            //console.log("index;"+ind+",nextIndex:"+nextIndex);
                            if (nextIndex > -1) {
                                txtcom2.innerHTML = ret.substring(j + 1, nextIndex);
                                ind = nextIndex;
                            }
                            else {
                                txtcom2.innerHTML = ret.substring(j + 1, ret.length);
                                ind = ret.length;
                            }

                            txtcom2.style.marginTop = "15px";
                            txtcom2.style.whiteSpace = "pre-wrap";
                            txtcom2.style.maxWidth = "700px";

                            this.$nextTick(() => {
                                var div = this.$refs["divrefs"][seq];
                                //console.log('divid:',seq,div);
                                div.appendChild(txtcom2);
                            })
                        }
                        else {
                            ind = ret.length;
                        }

                        //console.log("ind:"+ind);
                        //alert(ind);
                    }


                    // toastr.info("处理中，请稍等...")


                    //return ret
                    ret = "";
                    return "";
                },

                genFileName: function () {
                    var date = new Date()
                    console.log(date)
                    var year = date.getFullYear()//获取年
                    var month = date.getMonth() + 1//获取月
                    var day = date.getDate()//获取日
                    var hours = date.getHours()//获取时
                    var minutes = date.getMinutes()//获取分
                    var seconds = date.getSeconds()//获取秒
                    month = month > 9 ? month : "0" + month
                    day = day > 9 ? day : "0" + day
                    hours = hours > 9 ? hours : "0" + hours
                    minutes = minutes > 9 ? minutes : "0" + minutes
                    // seconds = seconds > 9 ? seconds : "0" + seconds//数据不大于9时在数据前方拼接一个“0”
                    var result = "施组_" + year + month + day + "_" + hours + minutes
                    return result
                },

                // load options
                newChild: function (item) {
                    app.configItem = item;
                    app.startConfig = true;
                    app.loadingOptions = true;
                    app.pickSubsTitle = item.name + " 配置";
                    if (item.options) {
                        app.loadingOptions = false;
                    } else {
                        // http get options
                        if (tokenInfo) {
                            const postStr = tokenInfo.substring(0, tokenInfo.length - 1) + `,"type":"` + projectType + `","lv":"` + item.lv + `","parent":"` + item.name + `"}`
                            const xhr = new XMLHttpRequest()
                            xhr.open('POST', baseUrl + '/tags')
                            xhr.setRequestHeader('Content-Type', 'application/json');
                            xhr.onreadystatechange = async function () {
                                app.loadingOptions = false;
                                if (xhr.readyState === 4) {
                                    if (xhr.status === 200) {
                                        // console.log('Server tags:', xhr.responseText);
                                        let resp = JSON.parse(xhr.responseText);
                                        if (resp.code === 200) {
                                            // app.configItem.options = resp.res;
                                            Vue.set(app.configItem, "options", resp.res);
                                            Vue.set(app.configItem, "children", []);
                                        } else {
                                            toastr.error(resp.msg);
                                        }
                                    } else {
                                        toastr.error('请求tags失败：' + xhr.statusText);
                                    }
                                }
                            }
                            xhr.send(postStr);
                        } else {
                            console.log('go login')
                            window.location.href = "./login.html"
                            app.loadingOptions = false;
                        }
                    }
                },
                handleChange: function (event) {
                    const isChecked = event.target.checked;
                    const value = event.target.value;
                    if (!isChecked) {
                        const index = app.configItem.children.indexOf(value);
                        if (index > -1) {
                            app.configItem.children.splice(index, 1);
                        }
                    }
                    // Vue.set(app.configItem, "children", app.configItem.selectedItems);
                    // console.log('Selected items:', app.configItem.children);
                }
            },
            mounted() {
                var tokenInfo = localStorage.getItem('tokenInfo')
                if (tokenInfo) {
                    var params = JSON.parse(tokenInfo)
                    const queryString = Object.keys(params)
                        .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
                        .join('&')
                    const xhr = new XMLHttpRequest()
                    xhr.open('POST', baseUrl + '/checkToken')
                    xhr.onload = async function () {
                        let res = JSON.parse(xhr.response);
                        console.log(res)
                        if (res.code === 200) {
                            console.log("验证token成功")
                            if (res.token) {
                                let tokenInfo = {
                                    username: name,
                                    token: res.token
                                }
                                localStorage.setItem('tokenInfo', JSON.stringify(tokenInfo))
                            }
                        } else {
                            // toastr.clear()
                            // toastr.error("登陆已过期，请重新登陆")
                            console.log('go login')
                            window.location.href = "./login.html"
                        }
                    }
                    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
                    xhr.send(queryString);
                } else {
                    // toastr.clear()
                    // toastr.error("请登陆")
                    console.log('go login')
                    window.location.href = "./login.html"
                }
            },
        })

        function createTableDom(jsonObj) {
            var tab = document.createElement('table');
            var tb = document.createElement('tbody');
            tab.style = "border: 1px dot light gray;width:100%;";
            var len = jsonObj.data.length;
            for (var i = 0; i < len; i++) {
                var tr = document.createElement("tr");
                var len2 = jsonObj.data[i].length;
                for (var j = 0; j < len2; j++) {
                    var td;
                    if (i == 0) {
                        td = document.createElement("th")
                    }
                    else {
                        td = document.createElement("td")
                    }
                    td.innerText = jsonObj.data[i][j];
                    // if(i == 0){
                    //     td.style = "width:25%;margin:0px;padding:0px;";
                    // }
                    tr.appendChild(td);
                }

                tb.appendChild(tr);
            }

            tab.appendChild(tb);

            return tab;
        }

        function genPDF() {
            toastr.info("处理中，请稍等...")
            document.getElementById('export_pdf').innerHTML = "处理中..."
            setTimeout(genPDFInner, 350)
        }

        function genPDFInner() {
            var doc = new jsPDF('p', 'mm', 'a4');

            function setFontSt_() {
                if ('simsun' === doc.fontName) {
                    console.log('already set ' + doc.fontName)
                    return
                }
                doc.addFileToVFS('simsun-normal.ttf', font_st);
                doc.addFont('simsun-normal.ttf', 'simsun', 'normal');
                doc.setFont('simsun', 'normal');
            }

            function setFontStb() {
                if ('font-simsun' === doc.fontName) {
                    console.log('already set ' + doc.fontName)
                    return
                }
                doc.addFileToVFS('font-simsun-bold.ttf', font_stb);
                doc.addFont('font-simsun-bold.ttf', 'font-simsun', 'bold');
                doc.setFont('font-simsun', 'bold');
            }

            doc.setLineHeightFactor(1.01);
            // doc.setLineSpacing(0.5);
            // doc.lineHeight = (16 / doc.internal.scaleFactor); // 将像素值转换为点

            const SIZE_CONTENT = 14;
            const SIZE_3 = SIZE_CONTENT;
            const SIZE_2 = SIZE_3 + 1;
            const SIZE_1 = SIZE_2 + 1;
            const SIZE_0 = 20;
            const START_Y = 25;

            let cursorY = START_Y;
            let fontSize = SIZE_1;
            let honPadding = 25;
            let catalogPageIndex = 1;
            let contentPageIndex = 2;
            // 获取 PDF 页面的宽度和高度
            let pdfWidth = doc.internal.pageSize.width;
            let pdfHeight = doc.internal.pageSize.height;
            console.log("pdfWidth: " + pdfWidth + ", pdfHeight:" + pdfHeight)
            //生成目录
            let cursorY_ = START_Y;
            let title = "目录"
            let titleWidth = doc.getStringUnitWidth(title) * doc.internal.getFontSize() / doc.internal.scaleFactor;
            doc.setFontSize(SIZE_0);
            setFontSt_();
            doc.text(title, (pdfWidth - titleWidth) / 2, cursorY_);
            cursorY_ += fontSize + 2;

            function updateCatalog(title) {
                if (cursorY_ > 280) {
                    doc.insertPage(catalogPageIndex + 1);
                    catalogPageIndex++;
                    contentPageIndex++;
                    cursorY_ = START_Y;
                }
                doc.setPage(catalogPageIndex)
                let currentFontSize = doc.getFontSize()
                let catalogSize = currentFontSize * 6 / 7;
                doc.setFontSize(catalogSize);
                doc.text(title, honPadding, cursorY_);
                // let txtW = doc.getStringUnitWidth(title) * doc.internal.getFontSize() / doc.internal.scaleFactor;
                // doc.link(honPadding, cursorY_, txtW, catalogSize, { destinationPageNumber: 1 });
                // doc.textWithLink(title, txtW, cursorY_, { destinationPageNumber: 2 });
                cursorY_ += SIZE_3;
                doc.setPage(contentPageIndex);
                doc.setFontSize(currentFontSize);
            }

            doc.addPage()
            // let outLen = app.retLi.length
            // let progress = 0
            app.retLi.forEach(function (obj, i) {
                let title = app.formatTitle(i, obj.alias || obj.name)
                if (i > 0) {
                    doc.addPage();
                    contentPageIndex++;
                    cursorY = START_Y; // 重置 Y 坐标位置为页面顶部
                }
                fontSize = SIZE_0;
                doc.setFontSize(fontSize);
                let titleWidth = doc.getStringUnitWidth(title) * doc.internal.getFontSize() / doc.internal.scaleFactor;
                doc.text(title, (pdfWidth - titleWidth) / 2, cursorY);
                updateCatalog(title);
                cursorY += SIZE_0 + SIZE_CONTENT;
                let content = app.formatModel(obj.content);
                fontSize = SIZE_CONTENT;
                doc.setFontSize(fontSize);
                let lines = doc.splitTextToSize(content, pdfWidth - 2 * honPadding);
                // let outProgress = i / outLen
                // let innerLen = lines.length
                for (let i = 0; i < lines.length; i++) {
                    if (cursorY > 280) { // 检查导致分页的 Y 坐标位置
                        doc.addPage();
                        contentPageIndex++;
                        cursorY = START_Y; // 重置 Y 坐标位置为页面顶部
                    }
                    //检查是否插入图片 {pdfImgName:"",width:10,height,10,url:""}
                    let imgIndex = lines[i].indexOf('{"pdfImgName');
                    let tableIndex = lines[i].indexOf('{"pdfTable');
                    let titleIndex = lines[i].indexOf('{#');
                    if (imgIndex !== -1) {
                        console.log("addImage")
                        let imgJson = "";
                        let i_ = i;
                        while (lines[i].indexOf("}") === -1) {
                            imgJson += lines[i].trim();
                            i++;
                        }
                        if (i > i_) {
                            let str = lines[i].trim()
                            imgJson += str.substring(0, str.indexOf("}")) + "}";
                        }
                        let imgObj = JSON.parse(imgJson);
                        // 定义图片宽度和高度
                        let imgWidth = imgObj.width > 0 ? imgObj.width : 100;
                        let imgHeight = imgObj.height > 0 ? imgObj.height : 100;
                        console.log("imgWidth:" + imgWidth + ", imgHeight:" + imgHeight)
                        let scaleW = 1;
                        let scaleH = 1;
                        let maxW = pdfWidth - honPadding * 2;
                        if (imgWidth > maxW) {
                            scaleW = maxW / imgWidth
                        }
                        let maxH = pdfHeight - honPadding * 2;
                        if (imgHeight > maxH) {
                            scaleH = maxH / imgHeight
                        }
                        let scale = scaleW < scaleH ? scaleW : scaleH;
                        console.log("scale:" + scale)
                        imgWidth = imgWidth * scale;
                        imgHeight = imgHeight * scale;
                        if (imgHeight > (pdfHeight - cursorY)) {
                            doc.addPage();
                            contentPageIndex++;
                            cursorY = START_Y;
                        }
                        // 计算图片需要插入的坐标位置，居中显示
                        let x = (pdfWidth - imgWidth) / 2;
                        // 插入图片到 PDF 中，并居中显示
                        let type = imgObj.src.substring(imgObj.src.indexOf("/") + 1, imgObj.src.indexOf(";"))
                        doc.addImage(imgObj.src, type, x, cursorY, imgWidth, imgHeight);
                        fontSize = SIZE_CONTENT - 4;
                        cursorY += imgHeight + fontSize;
                        doc.setFontSize(fontSize);
                        if (cursorY >= (pdfHeight - honPadding - fontSize)) {
                            doc.addPage();
                            contentPageIndex++;
                            cursorY = START_Y;
                        }
                        let imgTitleWidth = doc.getStringUnitWidth(imgObj.pdfImgName) * doc.internal.getFontSize() / doc.internal.scaleFactor;
                        doc.text(imgObj.pdfImgName, (pdfWidth - imgTitleWidth) / 2, cursorY);
                        cursorY += fontSize + 2;
                        fontSize = SIZE_CONTENT
                        doc.setFontSize(fontSize);
                        //图片处理结束
                    } else if (tableIndex !== -1) {
                        console.log("addTable")
                        let tableJson = "";
                        let i_ = i;
                        while (lines[i].indexOf("}") === -1) {
                            tableJson += lines[i].trim();
                            i++;
                        }
                        if (i > i_) {
                            let str = lines[i].trim()
                            tableJson += str.substring(0, str.indexOf("}")) + "}";
                            try {
                                tableJson = tableJson.replace(/[\n\r]/g, '')
                            } catch (e) {
                                alert(e.message);
                            }
                            // console.log('tableJson: ' + tableJson);
                        }
                        let tableObj = JSON.parse(tableJson);
                        let data = tableObj['data'];
                        let tableName = tableObj['pdfTableName'];
                        // let headers = data[0];
                        // data.shift();
                        let table = {
                            // head: [headers],
                            body: data,
                            startY: cursorY,
                            margin: honPadding,
                            theme: 'grid',
                            tableWidth: 'auto',
                            rowPageBreak: 'avoid',
                            bodyStyles: {
                                halign: 'center',
                                valign: 'middle',
                                minCellWidth: 15,
                                cellWidth: 'auto',
                            },
                            styles: {
                                font: 'simsun',
                                fontStyle: "normal",
                                fontSize: 12,
                            }
                        };
                        doc.autoTable(table);
                        let tableHeight = doc.lastAutoTable.finalY;
                        console.log(`tableHeight: ${tableHeight}`);
                        let pageNum = doc.internal.getNumberOfPages();
                        let inOnePage = pageNum === contentPageIndex;
                        if (!inOnePage) {
                            console.log("table multi page, pageNum:" + pageNum + ", offset: " + (pageNum - contentPageIndex));
                            contentPageIndex = pageNum;
                            cursorY = tableHeight;
                        } else {
                            cursorY = tableHeight;
                        }
                        if (cursorY > 280) {
                            doc.addPage();
                            contentPageIndex++;
                            cursorY = START_Y;
                        }
                        fontSize = SIZE_CONTENT - 4;
                        doc.setFontSize(fontSize);
                        cursorY += fontSize;
                        let tbNameWidth = doc.getStringUnitWidth(tableName) * doc.internal.getFontSize() / doc.internal.scaleFactor;
                        doc.text(tableName, (pdfWidth - tbNameWidth) / 2, cursorY);
                        cursorY += SIZE_CONTENT + 2;
                        fontSize = SIZE_CONTENT;
                        doc.setFontSize(fontSize);
                    } else if (titleIndex !== -1) {
                        let titleContent = ""
                        let index_ = lines[i].indexOf('{###');
                        let titleFontSize = SIZE_3;
                        if (index_ === -1) {
                            index_ = lines[i].indexOf('{##');
                            titleFontSize = SIZE_2;
                            if (index_ === -1) {
                                index_ = titleIndex;
                                titleFontSize = SIZE_1;
                                titleContent = lines[i].substring(index_ + 2, lines[i].indexOf('}'))
                            } else {
                                titleContent = lines[i].substring(index_ + 3, lines[i].indexOf('}'))
                            }
                        } else {
                            titleContent = lines[i].substring(index_ + 4, lines[i].indexOf('}'))
                        }
                        doc.setFontSize(titleFontSize);
                        if (cursorY > START_Y) {
                            if ((cursorY + 2 * SIZE_3) >= 280) {
                                doc.addPage();
                                contentPageIndex++;
                                cursorY = START_Y;
                            } else {
                                // cursorY += SIZE_3;
                            }
                        }
                        setFontStb();
                        doc.text(titleContent, honPadding, cursorY);
                        setFontSt_();
                        if (titleFontSize > SIZE_2) {
                            updateCatalog("    " + titleContent);
                        }
                        cursorY += SIZE_3;
                        doc.setFontSize(fontSize);
                    } else {
                        if (lines[i][0] === ' ' && lines[i].replaceAll(' ', '').length === 0) {
                            let unSetStr = lines[i];
                            let unSetStrLen = unSetStr.length;
                            console.log("skip blank line unSetStrLen:" + unSetStrLen);
                            if (unSetStrLen > 5) {
                                continue;
                            }
                            let j = i + 1;
                            let newStr = ' ' + unSetStr + lines[j];
                            if (newStr.length > 34) {
                                lines[j] = newStr.substring(34);
                                lines.splice(j, 0, newStr.substring(0, 34));
                            } else {
                                //lines[j] = newStr;
                                continue;
                            }
                        } else {
                            doc.text(lines[i], honPadding, cursorY); // 在当前页的给定位置添加文本
                            cursorY += fontSize + parseInt(app.linespace); // 递增 Y 坐标位置
                        }
                    }
                    // progress = outProgress + (i + 1) / (innerLen * outLen)
                    // document.getElementById('export_pdf').innerHTML = "处理中(" + (progress * 100) + "%)..."
                }
            })
            document.getElementById('export_pdf').innerHTML = "导出PDF"
            let result = app.genFileName()
            doc.save(result + ".pdf")
            toastr.clear()
            toastr.success("生成成功")
        }

        const arr = ["一", "二", "三", "四", "五", "六", "七", "八", "九", "十"]
        const first = "第"
        const ten = "十"
        const end = "章  "
        const end2 = "节  "

        const end3 = "、 "
        function formatTitle3(i, title) {
            if (i < 10) {
                return arr[i] + end3 + title
            }
            let high = Math.floor(i / 10)
            let low = i % 10
            console.log("ten:" + high + " Low: " + low)
            if (low === 9) {
                return arr[high] + ten + end3 + title
            }
            if (high === 1) {
                return ten + arr[low] + end3 + title
            }
            return arr[high - 1] + ten + arr[low] + end3 + title
        }

        //加载状态为complete时移除loading效果
        function completeLoading() {
            app.state = document.readyState
            console.log("onreadystatechange state: " + app.state)
        }

        //监听加载状态改变
        document.onreadystatechange = completeLoading;

        function logout() {
            localStorage.setItem('tokenInfo', "")
            window.location.href = "./login.html"
        }

    </script>

</body>

</html>